import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:flutter/foundation.dart';

import '../models/factory_model.dart';
import '../public.dart';
import './interceptors/error_interceptor.dart';
import 'interceptors/request_interceptor.dart';
import './interceptors/log_interceptor.dart';
import './interceptors/response_interceptor.dart';
import './api_code.dart';

///http请求
class HttpManager {
  static const CONTENT_TYPE_JSON = "application/json";
  static const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";

  var _dio = new Dio(); // 使用默认配置
  var _cache = DioCacheManager(CacheConfig(defaultMaxStale: Duration(days: 7)));

  HttpManager() {
    _dio.interceptors.add(_cache.interceptor);
    _dio.interceptors.add(new RequestInterceptors());
    _dio.interceptors.add(new LogsInterceptors());
    _dio.interceptors.add(new ErrorInterceptors());

    /// 返回数据处理，添加在此拦截器后面的response不会响应，这个最后添加
    _dio.interceptors.add(new ResponseInterceptors());
  }

  /// 发起网络请求
  /// [url] 请求url
  /// [data] 数据体
  /// [params] 请求参数
  /// [headers] 请求头
  /// [option] 配置
  /// [cancelToken] 取消请求
  /// [isParse] 是否进行Model实例解析，默认true, 进行Model实例化，false不进行Model实例化
  /// [forceRefresh] 强制刷新，不适用缓存
  /// [hasStatus] 是否需要状态，默认不需要，如果需要设置为ture,一遍解决需要状态码的情况和数据本身没有状态直接输出数据
  /// [isTip] 是否不提示错误
  Future<dynamic> fetch<T>(url,
      {String method = HttpMethodType.GET,
      data,
      Map<String, dynamic> queryParameters,
      Map<String, dynamic> headers,
      Options option,
      CancelToken cancelToken,
      bool isParse = true,
      bool forceRefresh = false,
      bool hasStatus = false,
      isTip = false}) async {
    Map<String, dynamic> _headers = {};
    _cache.clearAll();
    if (headers != null) {
      _headers.addAll(headers);
    }
    if (option != null) {
      option.headers = _headers;
    } else {
      option = Options();
      option.headers = _headers;
    }
    option.method = method;
    var biuldOptions = buildCacheOptions(
        Duration(days: method == HttpMethodType.GET ? AppConfig.CACHE_DAY : 0),
        options: option,
        forceRefresh: forceRefresh);

    Response response;
    try {
      response = await _dio.request(url,
          queryParameters: queryParameters,
          data: data,
          options: biuldOptions,
          cancelToken: cancelToken);
    } on DioError catch (e) {
      resultError(e, isTip);
      return null;
    }
    var resultData = response.data;
    if (resultData is DioError) {
      resultError(resultData, isTip);
      return null;
    }

    var resData = resultData.data;
    var resDataStatus = resData['status'];
    // 如果是没有状态码，且不需要解析数据直接将数据返回
    if (resDataStatus == null && !isParse) {
      return resData;
    }

    var _data = resData['data'];

    // 只有存在状态码数据才会有加密的数据进行解码。
    if (resData['isenc'] == 1 && _data != null) {
      _data = await compute(CryptoUtil.aesDecrypt, _data as String);
    }

    // 如果结果数据是字符串，且不为空则通过jsonDecode进行json处理
    if (_data.runtimeType.toString() == 'String' && _data.isNotEmpty) {
      _data = jsonDecode(_data);
    }

    if (resDataStatus == null) {
      _data = resData;
    }
    // 解析数据并返回
    if (isParse && _data != null) {
      _data = modelDataHanddle<T>(_data);
    }

    // 需要有状态
    if (hasStatus) {
      console(resData['message'], l1: resDataStatus);
      resData['data'] = _data;
      return resData;
    }
    // 不需要解析数据并返回
    return _data;
  }

  dynamic modelDataHanddle<T>(dynamic data) {
    if (data.runtimeType.toString() == 'List<dynamic>') {
      List<T> temp = [];
      data.toList().forEach((item) {
        temp.add(FactoryModel.getInstance<T>(item));
      });
      return temp;
    }
    return FactoryModel.getInstance<T>(data);
  }

  resultError(DioError e, noTip) {
    Response errorResponse;
    if (e.response != null) {
      errorResponse = e.response;
    } else {
      errorResponse = new Response(statusCode: 999);
    }
    if (e.type == DioErrorType.CONNECT_TIMEOUT ||
        e.type == DioErrorType.RECEIVE_TIMEOUT) {
      errorResponse.statusCode = ApiCode.NETWORK_TIMEOUT;
    }
    ApiCode.errorHandleFunction(errorResponse.statusCode, e.message, noTip);
  }
}

final HttpManager httpManager = HttpManager();
